package com.yuan.support;

import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.content.UriPermission;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.support.v4.provider.DocumentFile;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.List;

/**
 * DocumentFileUtils SDCardFragment 共用的方法均放在 DocumentFileUtils 里
 */
public class DocumentFileUtils {
    private static final String TAG = DocumentFileUtils.class.getSimpleName();

    // 是否开启日志
    public static boolean isLogOpen = true;

    public static final int REQUEST_PERMISSION_INTERVAL = 10 * 1000; // 请求权限的间隔时间
    public static long lastRequestTime = 0; // 上次的请求时间

    /**
     * todo 权限申请时机：
     * Activity的onResume时，判断是否有SD卡，判断是否已有写权限，有SD卡且没有写权限就申请，
     * 将Uri结果保存到SP并告知系统（避免需要重复申请），需要考虑拔出SD卡的情况，换SD卡的情况
     */
    public static void checkSDCardPermission(Activity activity) {

        // activity=com.yuan.support.MainActivity@3977
        // weakActivity=com.yuan.support.MainActivity@3977
        Log.d(TAG, "checkSDCardPermission: activity=" + activity);
        WeakReference<Activity> activityWeakReference = new WeakReference<>(activity);
        Activity weakActivity = activityWeakReference.get();
        Log.d(TAG, "checkSDCardPermission: weakActivity=" + weakActivity);

        if (weakActivity != null) {
            String sdRootPath = DocumentFileUtils.getSDRootPath();
            // 先判断是否有SD卡，避免无效申请
            if (!TextUtils.isEmpty(sdRootPath)) { // 不为空说明有SD卡

                // region法四
                // 1 先判断普通File是否可写
                File sdRootFile = new File(sdRootPath);
                boolean fileCanWrite = sdRootFile.canWrite();
                Log.d(TAG, "checkSDCardPermission: fileCanWrite=" + fileCanWrite);

                // region 避免重复弹框
                long currentTimeMillis = System.currentTimeMillis();
                long diff = currentTimeMillis - lastRequestTime;
                if (diff < REQUEST_PERMISSION_INTERVAL) {
                    Log.d(TAG, "checkSDCardPermission: diff=" + diff);
                    // 小于请求间隔时，不应弹框，故直接返回
                    return;
                } else {
                    // 大于请求间隔时，记录本次时间，继续其它判断操作
                    lastRequestTime = currentTimeMillis;
                }
                // endregion

                // SD卡是否可以直接用File写
                // if (!fileCanWrite) { // M2设备可写却报权限拒绝
                if (true) {
                    // 2 再判断 DocumentFile 是否可写，已含 RootTreeUri 的判断
                    DocumentFile rootDocumentFile = DocumentFileUtils.getSDRootDocumentFile();
                    Log.d(TAG, "checkSDCardPermission: rootDocumentFile=" + rootDocumentFile);
                    // rootDocumentFile=android.support.v4.provider.TreeDocumentFile
                    if (rootDocumentFile == null) {
                        // File 不可写， RootDocumentFile 为 null，说明 RootTreeUri 未保存，故需要重新申请
                        addAndShowFragment(weakActivity);
                    } else {
                        boolean canWrite = rootDocumentFile.canWrite();
                        Log.d(TAG, "checkSDCardPermission: DocumentFile canWrite=" + canWrite);
                        if (!canWrite) {
                            // RootTreeUri 保存了，RootDocumentFile 仍不可写？会有这种情况吗？故需要重新申请
                            addAndShowFragment(weakActivity);
                        }
                    }
                }
                // endregion
            }
        } else {
            Log.w(TAG, "checkSDCardPermission: weakActivity is null");
        }
    }

    private static void addAndShowFragment(Activity weakActivity) {
        SDCardFragment sdCardFragment = new SDCardFragment();

        if (weakActivity != null) {
            FragmentManager fragmentManager = weakActivity.getFragmentManager();
            fragmentManager.beginTransaction()
                    .add(sdCardFragment, SDCardFragment.class.getSimpleName())
                    .show(sdCardFragment) // todo add 和 show 可以一起
                    // .commitNowAllowingStateLoss() // api 24
                    .commitAllowingStateLoss();
        } else {
            Log.w(TAG, "addAndShowFragment: weakActivity is null");
        }
    }


    /**
     * 获取输出流
     * 已封装自动创建文件及中间目录
     * 兼容 File、兼容 DocumentFile
     * <p>
     * 比如 /storage/387A-1B14/DCIM/Camera/IMG_2222.jpg
     * documentFileUri = content://com.android.externalstorage.documents/tree/387A-1B14%3A/document/387A-1B14%3ADCIM%2FCamera%2FIMG_2222.jpg
     */
    public static OutputStream getOutputStream(String filePath) throws Exception {
        // 兼容 File
        File file = new File(filePath);
        // todo 特殊情况二 M2外置SD卡File可写但仍不能使用FileOutputStream
        //  所以 判断条件 由 外置SD卡File可写 变为 全路径是非SD卡路径
        // if (file.canWrite()) {
        filePath = file.getCanonicalPath();
        if (filePath.contains("/storage/emulated")) { // 含有这个的是非SD卡路径
            return new FileOutputStream(file);
        }

        // 兼容 DocumentFile
        Context context = ContextUtils.getContextByReflection();
        DocumentFile documentFile = getDocumentFile(filePath);
        Uri documentFileUri = documentFile.getUri();
        // content://com.android.externalstorage.documents/tree/387A-1B14%3A/document/387A-1B14%3ADCIM%2FCamera%2FIMG_2222.jpg
        Log.d(TAG, "getOutputStream: documentFileUri=" + documentFileUri);

        OutputStream outputStream = context.getContentResolver().openOutputStream(documentFileUri); // FileNotFoundException
        return outputStream;
    }

    /**
     * todo filePath经getCanonicalPath转换为fullPath后才与sdRootPath比较
     * 因为sdRootPath也是getCanonicalPath得到的，避免相对路径的影响
     * <p>
     * "/storage/387A-1B14/DCIM/Camera/IMG_20210602_153249.jpg"
     */
    public static DocumentFile getDocumentFile(String filePath) throws Exception {

        // 如果是路径，截掉尾部的/
        if (filePath.endsWith("/")) {
            Log.d(TAG, "getOutputStream: endsWith " + filePath);
            filePath = filePath.substring(0, filePath.length() - 1); // 截掉尾部的/
        }

        // todo 避免相对路径、全路径的影响
        File file = new File(filePath);
        String fullPath = file.getCanonicalPath(); // IOException
        Log.d(TAG, "getOutputStream: filePath=" + filePath
                + "\n fullPath=" + fullPath);

        String sdRootPath = getSDRootPath(); // /storage/387A-1B14

        // 如果 filePath 是SD卡根目录
        DocumentFile documentFile = getSDRootDocumentFile();

        // 如果 filePath 不是SD卡根目录
        if (!fullPath.equals(sdRootPath)) {
            // todo +1后从 /DCIM/Camera/IMG_20210602_153249.jpg 变为 DCIM/Camera/IMG_20210602_153249.jpg
            String relativePath = fullPath.substring(sdRootPath.length() + 1); // DCIM/Camera/IMG_20210602_153249.jpg
            String[] parts = relativePath.split("/"); // 按照“/”进行拆分

            Uri sdRootTreeUri = getSDRootTreeUri(); // content://com.android.externalstorage.documents/tree/387A-1B14%3A
            if (sdRootTreeUri == null) {
                throw new RuntimeException("user do not grant SDCard write permission");
            }
            String sdRootTreeDocumentId = DocumentsContract.getTreeDocumentId(sdRootTreeUri);
            Log.d(TAG, "getOutputStream: sdRootTreeDocumentId=" + sdRootTreeDocumentId); // 387A-1B14:

            // 判断中间文件夹是否存在，若不存在则创建
            DocumentFile tempDocumentFile = documentFile;
            for (int i = 0; i < parts.length - 1; i++) {
                // 找不到或者不存在才创建
                DocumentFile folder = tempDocumentFile.findFile(parts[i]);
                if (folder == null || !folder.exists()) {
                    tempDocumentFile = tempDocumentFile.createDirectory(parts[i]);
                } else {
                    // 找到了继续找下级目录
                    tempDocumentFile = folder;
                }
            }

            String fileName = parts[parts.length - 1];
            // 判断文件是否存在，若不存在才创建
            DocumentFile file1 = tempDocumentFile.findFile(fileName);
            if (file1 == null || !file1.exists()) {
                // 判断是文件夹还是文件，含"."时绝大多数是文件
                boolean isDirectory = !fileName.contains(".");
                if (isDirectory) {
                    tempDocumentFile = tempDocumentFile.createDirectory(fileName);
                } else {
                    tempDocumentFile = tempDocumentFile.createFile("", fileName);
                }
            } else {
                tempDocumentFile = file1;
            }

            documentFile = tempDocumentFile;
        }

        return documentFile;
    }

    /**
     * 获取SD卡根路径的DocumentFile
     *
     * @return 无SD卡时返回null
     */
    public static DocumentFile getSDRootDocumentFile() {
        Context context = ContextUtils.getContextByReflection();
        Uri sdRootTreeUri = getSDRootTreeUri();

        DocumentFile documentFile = null;
        if (sdRootTreeUri != null) {
            documentFile = DocumentFile.fromTreeUri(context, sdRootTreeUri);
        }
        return documentFile;
    }

    /**
     * 获取SD卡的根路径的写权限的Uri
     * 其中 uriString 是 content://com.android.externalstorage.documents/tree/387A-1B14%3A
     * <p>
     * todo 特殊情况一 M2设备 uriString 与 sdRootPath 不匹配
     * uriString 仍是 content://com.android.externalstorage.documents/tree/387A-1B14%3A
     * 但 sdRootPath 是 /storage/sdcard1
     * 授予SD写权限后拔出，仍能查到该权限，还能查到权限授予时的时间戳
     *
     * @return 无SD卡是返回null，有SD卡时也可能返回null，有SD卡且已授权且已保存时才返回非null
     */
    public static Uri getSDRootTreeUri() {
        Context context = ContextUtils.getContextByReflection();

        String sdRootPath = getSDRootPath(); // /storage/387A-1B14
        String sd = sdRootPath.replace("/storage/", ""); // 387A-1B14

        // todo 特殊情况 M2 不匹配
        if (sd.contains("sdcard")) {
            sd = "";
        }

        String uriString = null;
        List<UriPermission> persistedUriPermissions = context.getContentResolver().getPersistedUriPermissions();
        for (UriPermission uriPermission : persistedUriPermissions) {
            uriString = uriPermission.getUri().toString(); // content://com.android.externalstorage.documents/tree/387A-1B14%3A
            if (uriString.contains(sd)) {
                break;
            }
            uriString = null; // 避免 persistedUriPermissions 不含该SD卡根路径
        }

        Uri treeUri = null;
        if (uriString != null) {
            treeUri = Uri.parse(uriString);
        }
        return treeUri;
    }

    /**
     * 获取SD卡根路径
     *
     * @return 若存在SD卡，返回类似 /storage/387A-1B14 ，若不存在返回 null
     *
     * <p>
     * /storage/emulated/0/Android/data/com.cib.dgqb/files/external
     * /storage/387A-1B14/Android/data/com.cib.dgqb/files/external
     * <p>
     * todo 特殊情况四 修复拔出SD卡后M2的externalFiles会有一个null目录导致的一系列空指针异常问题
     */
    public static String getSDRootPath() {
        Context context = ContextUtils.getContextByReflection();
        String mSDCardRootPath = null;
        File[] externalFiles = context.getExternalFilesDirs("external");
        for (File externalFile : externalFiles) {
            String externalFilePath = null;
            try {
                if (externalFile != null) {
                    externalFilePath = externalFile.getCanonicalPath(); // IOException
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.i(TAG, "getSDRootPath: externalFile=" + externalFile
                    + "\n externalFilePath=" + externalFilePath);
            if (externalFilePath != null && !externalFilePath.contains("emulated")) { // 不含 emulated 的即为SD卡路径
                int indexOf = externalFilePath.indexOf("/Android/data/");
                mSDCardRootPath = externalFilePath.substring(0, indexOf);
                break;
            }
        }
        return mSDCardRootPath;
    }

    /**
     * 是否含有SD卡
     */
    public static boolean isHaveSDCard() {
        return !TextUtils.isEmpty(getSDRootPath());
    }

    /**
     * 将输入路径转化为非SD卡路径
     * 已考虑参数本身就是非SD卡路径的情况
     *
     * @param filePath
     * @return 如果filePath是SD卡上文件路径，则转换，否则输出filePath
     */
    public static String convertToNotSDFilePath(String filePath) {
        String sdRootPath = getSDRootPath();
        if (TextUtils.isEmpty(sdRootPath)) {
            return filePath;
        }
        String[] split = filePath.split(sdRootPath);
        String notSDFilePath = "/storage/emulated/0" + split[1];
        if (isLogOpen) {
            Log.d(TAG, "getNotSDFilePath: " + notSDFilePath);
        }
        return notSDFilePath;
    }
}
